home *** CD-ROM | disk | FTP | other *** search
- /*
- * $RCSfile: timer.C,v $
- * $Revision: 1.1.1.1 $
- * $Date: 1996/05/04 21:55:54 $
- */
- /**********************************************************************
- * EXODUS Database Toolkit Software
- * Copyright (c) 1991 Computer Sciences Department, University of
- * Wisconsin -- Madison
- * All Rights Reserved.
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation is hereby granted, provided that both the copyright
- * notice and this permission notice appear in all copies of the
- * software, derivative works or modified versions, and any portions
- * thereof, and that both notices appear in supporting documentation.
- *
- * THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY OF WISCONSIN --
- * MADISON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.
- * THE DEPARTMENT DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES
- * WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
- *
- * The EXODUS Project Group requests users of this software to return
- * any improvements or extensions that they make to:
- *
- * EXODUS Project Group
- * c/o David J. DeWitt and Michael J. Carey
- * Computer Sciences Department
- * University of Wisconsin -- Madison
- * Madison, WI 53706
- *
- * or exodus@cs.wisc.edu
- *
- * In addition, the EXODUS Project Group requests that users grant the
- * Computer Sciences Department rights to redistribute these changes.
- **********************************************************************/
- #include "ess.h"
- #include "checking.h"
- #include "sysdefs.h"
- #include "trace.h"
- #include "error.h"
- #include "list.h"
- #include "timer.h"
- #include "lock.h"
- #include "tid.h"
- #include "thread.h"
- #include "threadstate.h"
- #include "thread_globals.h"
- #include "thread_funcs.h" /* TODO: remove this line */
-
- Timer ServerTimer;
- static int ngettimeofday = 0;
-
- /*
- * similar to <sys/callout.h> but uses our list functions
- */
- typedef struct callout {
- LISTELEMENT list;
- TIME timeLeft; /* incremental time */
- TIMERFUNC func;
- TIMERARG arg; /* argument to routine */
- #ifdef DEBUG
- int cardinal; /* nth callout started */
- #endif DEBUG
- } CALLOUT;
-
-
- /* ******************************* CLOCK ******************************** */
-
- TIME
- Clock:: Elapsed(BOOL reset)
- {
- SELECTTIME currentTime;
- register TIME result;
- struct timezone timeZone;
-
- if(gettimeofday( ¤tTime, &timeZone) < 0) {
- SM_ERROR(TYPE_FATAL, errno);
- }
- ngettimeofday++;
- result = (TIME)(currentTime.tv_sec - timeOfLastTick.tv_sec) ;
-
- TRPRINT(TR_TEST, TR_LEVEL_1,
- ("CLOCK::Elapsed: @ %x, %d sec since last reset",
- currentTime.tv_sec, result));
-
- if(reset)
- timeOfLastTick = currentTime;
- return result;
- }
-
- TIME
- Clock::LastSleepTime()
- {
- return (TIME)(timeOfLastTick.tv_sec);
- }
-
- void
- Clock:: printStats()
- {
- fprintf(stdout, "Clock:\t last tick @ 0x%x (%d sec ago)\n",
- timeOfLastTick.tv_sec, Elapsed());
- }
-
- void
- Clock:: clearStats()
- {
- }
-
- #ifdef DEBUG
- void
- Clock:: dump()
- {
- printStats();
- }
- #endif DEBUG
-
- /* ******************************* TIMER ******************************** */
-
- #define enterTIMER \
- if(Active != NULL) {\
- if(Active->state == THREAD_TIMER_IN_USE) {\
- SM_ERROR(TYPE_FATAL, esmINTERNAL);\
- }\
- Active->state = THREAD_TIMER_IN_USE; \
- }
-
- void
- Timer:: Init( int maxTimers )
- /*
- * Not a constructor because we want to call it
- * after we get the options from the command line.
- */
- {
- CALLOUT *freeHeap;
-
- ncallout=0;
- INITIALIZELIST(&expiredList);
- INITIALIZELIST(&calloutList);
- INITIALIZELIST(&freeList);
-
- freeHeap = (CALLOUT *)calloc(maxTimers, sizeof(CALLOUT));
- for( nfree = 0; nfree < maxTimers; nfree++ ) {
- INITIALIZELISTELEMENT(freeHeap, &(freeHeap->list));
- listEnq( &freeList, &(freeHeap->list) );
- freeHeap++;
- }
-
- nstarted = ncalled = nremoved = nnotfound = 0;
-
- highwaterUsed = 0;
- }
-
- TIME
- Timer:: Tick(
- BOOL forceResetClock,
- BOOL checkForExpired
- )
- {
- TIME elapsedSec;
- TIME overExpired;
- CALLOUT *firstCallout;
-
-
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Tick (%d,%d)", forceResetClock,
- checkForExpired));
-
- #ifdef DEBUG
- firstCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
- if(ncallout > 0) {
- SM_ASSERT(LEVEL_1, firstCallout != NULL);
- } else {
- SM_ASSERT(LEVEL_1, firstCallout == NULL);
- }
- #endif DEBUG
-
- if(ncallout <= 0 ) {
- if(forceResetClock) Reset;
- goto Done;
- }
-
- nticks++;
-
- elapsedSec = Reset; /* now we MUST update the first element */
-
- TRPRINT(TR_TEST, TR_LEVEL_1, ("tick %d secs", elapsedSec));
-
- firstCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
- firstCallout->timeLeft -= elapsedSec;
-
- if( !checkForExpired )
- goto Done;
-
- while ((firstCallout != NULL) && (firstCallout->timeLeft <= 0)) {
- overExpired = 0-firstCallout->timeLeft;
-
- /*
- * Remove the callout from the timer list
- * and put it on the expired list
- */
- LISTREMOVE(&(firstCallout->list));
- ncallout --;
- LISTENQ(&expiredList, &(firstCallout->list));
- nexpired++;
-
- TRPRINT(TR_TEST, TR_LEVEL_1,
- ("Expired #%d, f=0x%x(a=0x%x)",
- firstCallout->cardinal, firstCallout->func, firstCallout->arg));
-
- if( (firstCallout =
- (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList)) != NULL) {
- firstCallout->timeLeft -= overExpired;
- }
- }
-
- if(firstCallout)
- SM_ASSERT(LEVEL_1, firstCallout->timeLeft > 0);
-
- enterTIMER;
-
- /*
- * Now, call all the funcs in the expired list
- */
- while (nexpired > 0) {
-
- SM_ASSERT(LEVEL_1, LIST_NOT_EMPTY(&expiredList));
-
- ncalled++;
-
- firstCallout = (CALLOUT *)listDeq(&expiredList);
- nexpired --;
-
- (*(firstCallout->func))(firstCallout->arg);
-
- listEnq(&freeList, &(firstCallout->list));
- nfree++;
- }
- Active->state = THREAD_ACTIVE;
- if( ncallout > 0) {
- firstCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList) ;
- SM_ASSERT(LEVEL_1, firstCallout->timeLeft > 0);
- }
-
- Done:
- SM_ASSERT(LEVEL_1, ncallout >= 0); /* sanity */
- if(ncallout != 0) {
-
- #ifdef DEBUG
- if( firstCallout->timeLeft < 0) {
- SM_ASSERT(LEVEL_1, checkForExpired == FALSE);
- }
- #endif DEBUG
-
- TRPRINT(TR_TEST, TR_LEVEL_1,
- ("Tick returns %d for callout #%d", firstCallout->timeLeft,
- firstCallout->cardinal));
- return firstCallout->timeLeft;
- } else {
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Tick returns 0"));
- return 0;
- }
- /*NOTREACHED*/
- }
-
- void
- Timer:: Start(
- TIME newTime,
- TIMERFUNC newFunc,
- TIMERARG newArg
- )
- {
- CALLOUT *newCallout, *thisCallout;
-
- enterTIMER;
-
- TRACE(TR_TEST, TR_LEVEL_1);
- /*
- * sanity check:
- * No sense in zero timers -may want to change this later.
- * No sense in timers > a week.
- */
- SM_ASSERT(LEVEL_1, (newTime > 0) );
-
- #define MINUTE 60
- #define HOUR (60*MINUTE)
- #define DAY (24*HOUR)
- #define WEEK (7*DAY)
- SM_ASSERT(LEVEL_1, (newTime <= WEEK) );
-
-
- /*
- * First, reset the clock so that it reflects "now",
- * and update the first element in the list (if it exists)
- * so that the new callout and the existing list of callouts
- * are relative to the same base time.
- */
-
-
- (void) Tick(TRUE /* reset the clock even if no items on the list */,
- FALSE /* do not check for expired timeouts */ );
-
- /*
- * Now get a new callout.
- */
- if(nfree <= 0) {
- SM_ERROR(TYPE_FATAL, esmNOFREETIMERS);
- }
- nstarted++;
- newCallout = (CALLOUT *)listDeq(&freeList);
- nfree--;
- highwaterUsed = (highwaterUsed < ++ncallout) ? ncallout : highwaterUsed;
-
- TRPRINT(TR_TEST, TR_LEVEL_1,
- ("starting func 0x%x(arg 0x%x) with time %d", newFunc, newArg, newTime));
- newCallout->timeLeft = newTime;
- SM_ASSERT(LEVEL_1, newCallout->timeLeft >= 0);
- newCallout->func = newFunc;
- newCallout->arg = newArg;
-
- #ifdef DEBUG
- newCallout->cardinal = nstarted;
- #endif DEBUG
-
-
- /*
- * Put the timer in the list. The clock reflects "now", so all the
- * relative times are legit wrt "now", which means that some may be
- * negative.
- */
- for(
- thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
- thisCallout != NULL;
- thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list) ) ) {
-
- if (thisCallout->timeLeft > newCallout->timeLeft) {
- thisCallout->timeLeft -= newCallout->timeLeft;
-
- /*
- * put it at the END of the (circular) list,
- * which is to say, before thisCallout element.
- */
- listEnq(&(thisCallout->list), &(newCallout->list));
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Start() returns"));
- Active->state = THREAD_ACTIVE;
- return;
- }
- newCallout->timeLeft -= thisCallout->timeLeft;
-
- SM_ASSERT(LEVEL_1, newCallout->timeLeft >= 0);
- }
- /*
- * We went throught the entire list (or found the list null).
- * This element goes on the end.
- */
- listEnq(&(calloutList), &(newCallout->list));
-
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Start() returns"));
- if(Active)
- Active->state = THREAD_ACTIVE;
- return;
- }
-
-
- BOOL
- Timer:: Remove(
- TIMERFUNC func,
- TIMERARG arg
- )
- {
- CALLOUT *thisCallout, *nextCallout;
-
- enterTIMER;
-
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Remove( func 0x%x arg 0x%x)", func, arg));
- nremoved++;
-
- for(
- thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
- thisCallout != NULL;
- thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list) ) ) {
-
- if( thisCallout->func == func && thisCallout->arg == arg ) {
- TRPRINT(TR_TEST, TR_LEVEL_1,
- ("removed func 0x%x(arg 0x%x), had time %d", func, arg,
- thisCallout->timeLeft));
-
- nextCallout = (CALLOUT *)NEXT_LIST_ELEMENT(&(thisCallout->list));
- if(nextCallout) {
- nextCallout->timeLeft += thisCallout->timeLeft;
- SM_ASSERT(LEVEL_1, nextCallout->timeLeft >= 0);
- }
-
- ncallout--;
- listMoveEnq(&freeList, &(thisCallout->list));
- nfree ++;
-
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Remove returns (found)"));
-
- Active->state = THREAD_ACTIVE;
- return TRUE;
- }
- }
- nnotfound++;
- TRPRINT(TR_TEST, TR_LEVEL_1, ("Remove returns (not found)"));
- Active->state = THREAD_ACTIVE;
- return FALSE;
- }
-
- void
- Timer:: PrintStats()
- {
- if(nstarted>0) {
- fprintf(stdout,"TIMERS:\t%2d%% usage highwater, %2d%% expiring, %2d%% free (now)\n",
- (100*highwaterUsed)/(ncallout+nfree+nexpired),
- (100*nexpired)/(ncallout+nfree+nexpired),
- (100*nfree)/(ncallout+nfree+nexpired) );
- fprintf(stdout,"\t%8d ticks, %8d started\n", nticks, nstarted);
- fprintf(stdout,"\t%8d called, %8d removed (%8d not found)\n",
- ncalled, nremoved, nnotfound);
-
- } else {
- fprintf(stdout,"Timers:\tNo timers used yet.\n");
- }
-
-
- printStats();
- }
-
- void
- Timer:: ClearStats()
- {
- nticks = nstarted = ncalled = nremoved = nnotfound = 0;
- /* cannot touch ncallout or nfree */
- clearStats();
- }
-
- #ifdef DEBUG
-
- void
- Timer:: Dump(int line, char * file)
- {
- CALLOUT *thisCallout;
- register int n;
-
- TRPRINT(TR_TEST, TR_LEVEL_1, ("DUMP from %20.20s line %d", file, line));
-
- for(
- (n=0), thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&calloutList);
- thisCallout != NULL;
- n++, thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list))){
-
- TRPRINT(TR_TEST, TR_LEVEL_1,
- ("%10d sec : #%d f=0x%x(a=0x%x)",
- thisCallout->timeLeft, thisCallout->cardinal,
- thisCallout->func, thisCallout->arg));
- }
- if(n != ncallout) {
- TRPRINT(TR_TEST, TR_LEVEL_1, ("ERROR: n %d, n %d", n, ncallout));
- }
- for(
- (n=0), thisCallout = (CALLOUT *) FIRST_LIST_ELEMENT(&freeList);
- thisCallout != NULL;
- n++, thisCallout = (CALLOUT *)NEXT_LIST_ELEMENT( &(thisCallout->list))){
- }
- if(n != nfree) {
- TRPRINT(TR_TEST, TR_LEVEL_1, ("ERROR: n %d, nfree %d", n, nfree));
- }
-
- dump();
- }
-
- void
- DumpTimers()
- {
- ServerTimer.Dump(__LINE__, __FILE__);
- }
-
- #endif DEBUG
-